//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright> 
//------------------------------------------------------------------------------

using System;
using System.IO;
using System.Reflection;
using System.Web;
using CommunityServer.Components;
using CommunityServer.Configuration;
using System.Text.RegularExpressions;

namespace CommunityServer 
{

    // *********************************************************************
    //  CSHttpModule
    //
    /// <summary>
    /// This HttpModule encapsulates all the forums related events that occur 
    /// during ASP.NET application start-up, errors, and end request.
    /// </summary>
    // ***********************************************************************/
    public class CSHttpModule : IHttpModule 
    {
        #region Member variables and inherited properties / methods

        public String ModuleName 
        { 
            get { return "CSHttpModule"; } 
        }

        private string authenticationType = string.Empty;


        #region IHttpModule
        // *********************************************************************
        //  ForumsHttpModule
        //
        /// <summary>
        /// Initializes the HttpModule and performs the wireup of all application
        /// events.
        /// </summary>
        /// <param name="application">Application the module is being run for</param>
        public void Init(HttpApplication application) 
        { 
            // Wire-up application events
            //
            application.BeginRequest += new EventHandler(this.Application_BeginRequest);
            application.BeginRequest +=new EventHandler(cs_beginrequest);
            application.AuthenticateRequest += new EventHandler(Application_AuthenticateRequest);
            application.Error += new EventHandler(this.Application_OnError);
            application.AuthorizeRequest += new EventHandler(this.Application_AuthorizeRequest);
            application.EndRequest += new EventHandler(this.Application_EndRequest);
        }

        private void cs_beginrequest(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            HttpContext context = application.Context;

            context.Response.AddHeader("CommunityServer", SiteStatistics.CommunityServerVersion.ToString());
        }


        //int settingsID;
        public void Dispose() 
        {
        }
        #endregion

		#region Installer




		#endregion


        #endregion

        #region Application OnError
        private void Application_OnError (Object source, EventArgs e) 
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            
			CSException csException = context.Server.GetLastError() as CSException;

			if(csException == null)
				csException = context.Server.GetLastError().GetBaseException() as CSException;

			try
			{
				if (csException != null)
				{
					switch (csException.ExceptionType) 
					{
						case CSExceptionType.UserInvalidCredentials:
						case CSExceptionType.AccessDenied:
						case CSExceptionType.AdministrationAccessDenied:
						case CSExceptionType.ModerateAccessDenied:
						case CSExceptionType.PostDeleteAccessDenied:
						case CSExceptionType.PostProblem:
						case CSExceptionType.UserAccountBanned:
						case CSExceptionType.ResourceNotFound:
						case CSExceptionType.UserUnknownLoginError:
						case CSExceptionType.SectionNotFound:
						case CSExceptionType.LicenseAccessError:
							csException.Log();
							break;
					}
				} 
				else 
				{
					Exception ex = context.Server.GetLastError();
					if(ex.InnerException != null)
						ex = ex.InnerException;

					csException = new CSException(CSExceptionType.UnknownError, ex.Message, context.Server.GetLastError());

                    System.Data.SqlClient.SqlException sqlEx = ex as System.Data.SqlClient.SqlException;
                    if(sqlEx == null || sqlEx.Number != -2) //don't log time outs
    					csException.Log();
				}
			}
			catch{} //not much to do here, but we want to prevent infinite looping with our error handles

			CSEvents.CSException(csException);
        }


        #endregion


        #region Application AuthenticateRequest

        private void Application_AuthenticateRequest(Object source, EventArgs e) 
        {
            HttpContext context = HttpContext.Current;
            Provider p = null;
            ExtensionModule module = null;

			// If the installer is making the request terminate early
			if (CSConfiguration.GetConfig().AppLocation.CurrentApplicationType == ApplicationType.Installer) {
				return;
			}
			
			// Only continue if we have a valid context
            //
                       if (context == null)
                return;
			
		    authenticationType = String.Empty;
			
            // If the current context user object is null, it doesn't necessarily mean that
            // that we don't have a valid user, just that we don't have a user object that 
            // ASP.Net knows about natively, I.E. a user with an existing FormsAuthentication 
            // or WindowsAuthentication ticket.  If the CookieAuthentication add-on module is
            // being used, the authentication ticket will be the cookie itself, so if the user
            // is null, we need to see if the CookiesAuthentication module can be loaded.
            if (context.User == null)
			{
					p = (Provider) CSConfiguration.GetConfig().Extensions["CustomAuthentication"];
					module = ExtensionModule.Instance(p);
					if(module != null)
						authenticationType = "customidentity";
					else
						return;
			}
			else
			{
				authenticationType = context.User.Identity.GetType().Name.ToLower();
			}


				try 
				{
					// Logic to handle various authentication types
					//
					switch(authenticationType)
				{

                        // Microsoft passport
					case "passportidentity":
						p = (Provider) CSConfiguration.GetConfig().Extensions["PassportAuthentication"];
                        module = ExtensionModule.Instance(p);
						if(module != null)
							module.ProcessRequest();
						else
							goto default;
						break;

                        // Windows
					case "windowsidentity":
						p = (Provider) CSConfiguration.GetConfig().Extensions["WindowsAuthentication"];
                        module = ExtensionModule.Instance(p);
						if(module != null)
							module.ProcessRequest();
						else
							goto default;
						break;

						// Forms
					case "formsidentity":
						p = (Provider) CSConfiguration.GetConfig().Extensions["FormsAuthentication"];
						module = ExtensionModule.Instance(p);
						if(module != null)
							module.ProcessRequest();
						else
							goto default;
						break;

						// Custom
					case "customidentity":
						p = (Provider) CSConfiguration.GetConfig().Extensions["CustomAuthentication"];
						module = ExtensionModule.Instance(p);
						if(module != null)
							module.ProcessRequest();
						else
							goto default;
						break;

                    default:
                        CSContext.Current.UserName = context.User.Identity.Name;
                        break;

                }

            } 
            catch( Exception ex ) 
            {
                CSException forumEx = new CSException( CSExceptionType.UnknownError, "Error in AuthenticateRequest", ex );
                forumEx.Log();

                throw forumEx;
            }

            //			// Get the roles the user belongs to
            //			//
            //			Roles roles = new Roles();
            //			roles.GetUserRoles();
        }
        #endregion

        #region Application AuthorizeRequest
        private void Application_AuthorizeRequest (Object source, EventArgs e) {


			if (CSConfiguration.GetConfig().AppLocation.CurrentApplicationType == ApplicationType.Installer)
			{
				//CSContext.Create(context);
				return;
			}


            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

			CSContext csContext = CSContext.Current;
            //bool enableBannedUsersToLogin = CSContext.Current.SiteSettings.EnableBannedUsersToLogin;
            
//			// If the installer is making the request terminate early
//			if (csContext.ApplicationType == ApplicationType.Installer) {
//				return;
//			}

            //csContext.User = CSContext.Current.User;

			CSEvents.UserKnown(csContext.User);

            ValidateApplicationStatus(csContext);

            // Track anonymous users
            //
			//Users.TrackAnonymousUsers(context);

            // Do we need to force the user to login?
            //
			
            if (context.Request.IsAuthenticated) {
                string username = context.User.Identity.Name;
                if (username != null) {
                    string[] roles = CommunityServer.Components.Roles.GetUserRoleNames(username);
                    if (roles != null && roles.Length > 0) {
                        csContext.RolesCacheKey = string.Join(",",roles);
                    }
                }
            }
        }

        #endregion

        #region Application BeginRequest
		private void Application_BeginRequest(Object source, EventArgs e) 
		{
			HttpApplication application = (HttpApplication)source;
			HttpContext context = application.Context;

			if (context.Request.RawUrl.IndexOfAny(new char[] {'<', '>', '\'', '"'}) != -1)
			{
				context.Response.Redirect(context.Request.RawUrl.Replace("<", "%3c").Replace(">", "%3e").Replace("\'", "%27").Replace("\"", "%22"));
				return;
				// throw new CSException(CSExceptionType.GeneralAccessDenied, "The URL is invalid.  Processing has been aborted.");
			}

			CSConfiguration config = CSConfiguration.GetConfig();
			
			// If the installer is making the request terminate early
			if (config.AppLocation.CurrentApplicationType == ApplicationType.Installer)
			{
				//CSContext.Create(context);
				return;
			}

			CheckWWWStatus(config,context);
		
            CheckSSL(config.SSL,context);
		
			CSContext.Create(context, new UrlReWriterDelegate(ReWriteUrl));
		}

        private void CheckSSL(SSLSettings ssl, HttpContext context)
        {
            if(ssl != SSLSettings.All)
                return;

            Globals.RedirectToSSL(context);


        }

        private static Regex wwwStatusRegex = new Regex("https?://www\\.", RegexOptions.IgnoreCase | RegexOptions.Compiled);
        private static Regex noWwwStatusRegex = new Regex("https?://", RegexOptions.IgnoreCase | RegexOptions.Compiled);
        private static readonly string withWWW = "{0}://www.";
        private static readonly string noWWW = "{0}://";

		private void CheckWWWStatus(CSConfiguration config, HttpContext context)
		{
			if(config.WWWStatus == WWWStatus.Ignore)
				return;

			string rawUrl = context.Request.Url.ToString();
            bool isWWW = wwwStatusRegex.IsMatch(rawUrl); 

			
			if(config.WWWStatus == WWWStatus.Remove && isWWW)
			{
                context.Response.Redirect(wwwStatusRegex.Replace(rawUrl, string.Format(noWWW,context.Request.Url.Scheme)));
			}
			else if(config.WWWStatus == WWWStatus.Require && !isWWW)
			{
                context.Response.Redirect(noWwwStatusRegex.Replace(rawUrl, string.Format(withWWW, context.Request.Url.Scheme)));
			}
			
		
		}

		#region ReWriteUrl
		private bool ReWriteUrl(HttpContext context)
		{
            UrlReWriteProvider urlProvider = UrlReWriteProvider.Instance();
			string path = context.Request.Path;
			string newPath = UrlReWriteProvider.Instance().GetRewrittenUrl(path,context.Request.Url.Query);
            			
			if(newPath != null)
			{
				string qs = null;
				int index = newPath.IndexOf('?');
				if (index >= 0)
				{
					qs = (index < (newPath.Length - 1)) ? newPath.Substring(index + 1) : string.Empty;
					newPath = newPath.Substring(0, index);
				}

                urlProvider.RewriteUrl(context, newPath, qs); 
			}

			return newPath != null;
		}

		#endregion

        private void ValidateApplicationStatus(CSContext cntx)
        {
            if(!cntx.User.IsAdministrator)
            {
                string disablePath = null;
                CSApplicationData data = ApplicationSet.Applications[cntx.Config.AppLocation.CurrentApplicationType];
                if(data != null && !data.Enabled)
                {
                    switch(cntx.Config.AppLocation.CurrentApplicationType)
                    {
                        case ApplicationType.Forum:
                                disablePath = "ForumsDisabled.htm";
                            break;
                        case ApplicationType.Weblog:
                                disablePath = "BlogsDisabled.htm";
                            break;
                        case ApplicationType.Gallery:
                                disablePath = "GalleriesDisabled.htm";
                            break;
                        case ApplicationType.FeedReader:
                                disablePath = "FeedReaderDisabled.htm";
                            break;
                        case ApplicationType.BlogRoller:
                                disablePath = "BlogRollerDisabled.htm";
                            break;
						case ApplicationType.FileGallery:
							disablePath = "FileGalleriesDisabled.htm";
							break;
					}

                    if(disablePath != null)
                    {
                        string errorpath = cntx.Context.Server.MapPath(string.Format("~/Languages/{0}/errors/{1}",cntx.Config.DefaultLanguage,disablePath));
                        using(StreamReader reader = new StreamReader(errorpath))
                        {
                            string html = reader.ReadToEnd();
                            reader.Close();

                            cntx.Context.Response.Write(html);
                            cntx.Context.Response.End();
                        }
                    }
                }
            }
        }

        #endregion
        
        #region Application EndRequest
        private void Application_EndRequest (Object source, EventArgs e) 
        {
            HttpContext context = HttpContext.Current;
            //  If the application is using a custom configuration module, we won't have the 
            //  built-in handling for unauthorized users that ASP.Net provides for forms authentication
            //  so we'll deal with requests from unauthorized users here.  
            if (authenticationType == "customidentity" && context.Response.StatusCode == 401 && context.Request.QueryString["ReturnUrl"] == null)
            {
          
                string redirectUrl = String.Empty;
                redirectUrl = SiteUrls.FormatUrlWithParameters(SiteUrls.Instance().UrlData.Paths["login_clean"], 
                    "ReturnUrl=" + Globals.UrlEncode(context.Request.Url.ToString()));
                context.Response.Redirect(redirectUrl);

            }
        }
        #endregion


    }

}
